package service

import (
	"errors"
	"gitee.com/sansaniot/ssiot-core/httpmvc/service"
	mapset "github.com/deckarep/golang-set/v2"
	"gorm.io/gorm"
	"ssadmin/common/constant"
	cDto "ssadmin/common/query"
	"ssadmin/common/utils"
	"ssadmin/internal/admin/models"
	"ssadmin/internal/admin/models/command"
	"ssadmin/internal/admin/models/query"
)

type Menu struct {
	service.Service
}

type MenuIdResult struct {
	MenuId string `json:"menu_id"`
}

// GetBySystem 获取Menu对象
func (e *Menu) GetBySystem(c *query.MenuQuery, list *[]models.Menu) error {
	data := models.Menu{}
	menuIds := make([]string, 0)

	//根据用户权限查菜单（admin查询所有）
	if len(c.UserId) > 0 && c.UserId != constant.AdminUser {
		menuIdResult := make([]MenuIdResult, 0)
		sql := "SELECT DISTINCT rm.menu_id  FROM sys_role_menu_r rm" +
			" LEFT JOIN sys_role r ON ( rm.role_id = r.id )" +
			" LEFT JOIN sys_user_role_r ur ON ( ur.role_id = r.id )" +
			" LEFT JOIN sys_user u ON ( u.id = ur.user_id )" +
			"  WHERE u.id = ?"
		e.Orm.Raw(sql, c.UserId).Scan(&menuIdResult)
		if len(menuIdResult) == 0 {
			return nil
		}
		for _, v := range menuIdResult {
			menuIds = append(menuIds, v.MenuId)
		}
		createMenuIds := make([]string, 0)
		sql = "SELECT DISTINCT id FROM sys_menu WHERE creator_id = ?"
		e.Orm.Raw(sql, c.UserId).Scan(&createMenuIds)
		menuIds = append(menuIds, createMenuIds...)
	}

	where := cDto.MakeCondition(c.GetNeedSearch(c.Filter, menuIds...))
	err := data.FindList(e.Orm, &list, where)

	for i, info := range *list {
		(*list)[i].ModifyTime = info.ModifyTimeDts.Format(utils.TIME_FMT_FULL)
		(*list)[i].CreateTime = info.CreateTimeDts.Format(utils.TIME_FMT_FULL)
	}
	return err
}

// Insert 创建Menu对象
func (e *Menu) Insert(c *command.MenuInsertReq) error {
	data := models.Menu{}
	c.Generate(&data)
	return e.Orm.Transaction(func(tx *gorm.DB) error {
		if err := data.Add(tx, &data); err != nil {
			return err
		}
		// 用户创建菜单不可见，需要自动跟角色绑定(admin可见所有)
		if c.CreatorId != constant.AdminUser {
			ru := models.UserRole{}
			urList := make([]models.UserRole, 0)

			if err := ru.FindListByConds(tx, &urList, "user_id=?", c.CreatorId); err != nil {
				return err
			}

			rm := models.RoleMenu{
				RoleId:        ru.RoleId,
				MenuId:        data.MenuId,
				DefaultStatus: "0",
			}

			if err := rm.Add(tx, &rm); err != nil {
				return err
			}
		}

		return nil
	})
}

// Update 修改Menu对象
func (e *Menu) Update(c *command.MenuUpdateReq) error {
	if len(c.MenuId) == 0 {
		return errors.New("菜单ID不能为空")
	}

	menuModel := models.Menu{}

	id := query.MenuQuery{MenuId: c.MenuId}
	where := cDto.MakeCondition(id)
	if err := menuModel.FindOne(e.Orm, &menuModel, where); err != nil {
		return err
	}

	updateSubMenu := true
	if c.Enable == "" || c.Enable == menuModel.Enable {
		updateSubMenu = false
	}

	c.Generate(&menuModel)

	modIds := mapset.NewSet[string]()
	e.getChildIds(c.MenuId, modIds)

	return e.Orm.Transaction(func(tx *gorm.DB) error {
		if err := menuModel.Mod(tx, &menuModel, where); err != nil {
			return err
		}

		if updateSubMenu == false {
			return nil
		}

		if err := tx.Table("sys_menu").Where("id in (?)", modIds.ToSlice()).Update("enable", c.Enable).Error; err != nil {
			return err
		}

		return nil
	})
}

// EditOrder 修改Menu对象
func (e *Menu) EditOrder(c []command.MenuOrderReq) error {
	data := models.Menu{}
	err := e.Orm.Transaction(func(tx *gorm.DB) error {
		for _, req := range c {
			fail := data.Mod(tx, &models.Menu{
				ParentId: req.ParentId,
				MenuId:   req.MenuId,
				OrderNum: req.OrderNum,
			})
			if fail != nil {
				return fail
			}
		}
		return nil
	})

	return err
}

// Remove 删除Menu
func (e *Menu) Remove(d *command.MenuDeleteReq) error {
	if len(d.GetId()) == 0 {
		return nil
	}

	txErr := e.Orm.Transaction(func(tx *gorm.DB) error {
		data := models.Menu{}
		deleteIds := mapset.NewSet[string]()
		for _, id := range d.GetId() {
			e.getChildIds(id, deleteIds)
		}

		if err := data.Del(tx, "id in (?)", deleteIds.ToSlice()); err != nil {
			return err
		}

		//删除角色菜单
		rm := models.RoleMenu{}
		if err := rm.Del(tx, &rm, "menu_id in (?)", deleteIds.ToSlice()); err != nil {
			return err
		}

		return nil
	})

	return txErr
}

func (e *Menu) getChildIds(id string, idList mapset.Set[string]) {
	idList.Add(id)
	data := models.Menu{}
	childList := make([]models.Menu, 0)
	_ = data.FindListByConds(e.Orm, &childList, "parent_id=?", id)

	for _, menu := range childList {
		e.getChildIds(menu.MenuId, idList)
	}
}
